package org.wzy.spring.framework.webmvc.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;

/**
 * 模拟 SpringMVC HandlerAdapter 接口实现, 专人干专事
 *
 * @author 王政豫
 * @date 2018/5/22 14:43
 */
class HandlerAdapter {

    /**
     * 保存方法及其参数下标
     */
    private Map<String, Integer> parameterMap;

    HandlerAdapter(Map<String, Integer> parameterMap) {
        this.parameterMap = parameterMap;
    }

    /**
     * 模拟 SpringMVC HandlerAdapter 接口中的同名方法实现, 处理方法参数列表, 并反射调用该方法
     *
     * @param request  request
     * @param response response
     * @param handler  handler 中包含了 Controller、method、url 信息
     * @return ModelAndView
     * @author wzy_h
     * @date 2018/5/23 21:50
     * @since 1.8
     */
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws InvocationTargetException, IllegalAccessException {
        /* 1.准备此方法的形参列表 */
        // 形参的决定因素: 参数的个数、类型、顺序、方法名称
        HandlerMapping handlerMapping = (HandlerMapping) handler;
        Method method = handlerMapping.getMethod();
        Class<?>[] parameterTypes = method.getParameterTypes();

        /* 2.获取自定义参数所在下标 */
        Map<String, String[]> parameterMap = request.getParameterMap();

        /* 3.构造实参列表 */
        Object[] parameterValues = new Object[parameterTypes.length];
        for (Map.Entry<String, String[]> paramEntry : parameterMap.entrySet()) {
            String paramValue = Arrays.toString(paramEntry.getValue()).replaceAll("\\[|\\]", "").replaceAll("\\s", "");
            if (this.parameterMap.containsKey(paramEntry.getKey())) {
                Integer paramIndex = this.parameterMap.get(paramEntry.getKey());
                parameterValues[paramIndex] = convertStringValuesToOriginal(paramValue, parameterTypes[paramIndex]);
            }
        }
        if (this.parameterMap.containsKey(HttpServletRequest.class.getName())) {
            Integer paramIndex = this.parameterMap.get(HttpServletRequest.class.getName());
            parameterValues[paramIndex] = parameterValues[paramIndex];
        } else if (this.parameterMap.containsKey(HttpServletResponse.class.getName())) {
            Integer paramIndex = this.parameterMap.get(HttpServletResponse.class.getName());
            parameterValues[paramIndex] = parameterValues[paramIndex];
        }

        /* 4.从 handler 中取出 controller、method, 然后利用反射机制调用 */
        Object result = method.invoke(handlerMapping.getController(), parameterValues);
        if (null == result) {
            return null;
        }

        // 判断返回值是否为 ModelAndView
        boolean isModelAndView = method.getReturnType() == ModelAndView.class;
        if (isModelAndView) {
            return (ModelAndView) result;
        }
        return null;
    }

    /**
     * 根据 HandlerAdapter 中的方法参数下标找到对应的参数
     * 注意, 在获取到参数之后需要进行转换工作, 在 SpringMVC 中是根据各种 MessageConverter 来进行转换的, 可以参照 MappingJackson2HttpMessageConverter
     *
     * @return 转换后的参数值
     * @author wzy_h
     * @date 2018/5/24 21:52
     * @since 1.8
     */
    private Object convertStringValuesToOriginal(String value, Class<?> clz) {
        if (clz == String.class) {
            return value;
        } else if (clz == Integer.class) {
            return Integer.valueOf(value);
        } else if (clz == int.class) {
            return Integer.valueOf(value);
        } else {
            return null;
        }
    }
}
